package com.king.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.king.entity.MPost;
import com.king.mapper.MPostMapper;
import com.king.service.MPostService;
import com.king.util.RedisUtil;
import com.king.vo.MPostVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author KING
 * @since 2020-09-01
 */
@Service
public class MPostServiceImpl extends ServiceImpl<MPostMapper, MPost> implements MPostService {

    @Autowired
    MPostMapper mPostMapper;

    @Autowired
    RedisUtil redisUtil;

    @Override
    public IPage<MPostVo> paging(Page page, Long categoryId, Long userId, Integer level, Boolean recommend, String order) {

        if (level == null) level = -1;

        QueryWrapper wrapper = new QueryWrapper<MPost>()
                .eq(categoryId != null, "category_id", categoryId)
                .eq(userId != null, "user_id", userId)
                .eq(level == 0, "level", 0)
                .gt(level > 0, "level", 0)
                .eq(recommend != null, "recommend", recommend)
                .orderByDesc(order != null, order);

        return mPostMapper.selectPosts(page, wrapper);
    }

    @Override
    public MPostVo selectOnePost(QueryWrapper<MPost> wrapper) {
        return mPostMapper.selectOnePost(wrapper);
    }

    /**
     * 本周热议
     */
    @Override
    public void initWeekRank() {
        //1.获取7天内发表的文章（严格来说是评论过的文章）
        List<MPost> posts = this.list(new QueryWrapper<MPost>()
                .ge("created", DateUtil.offsetDay(new Date(), -7))
                .select("id,title,user_id,comment_count,view_count,created")
        );

        if(posts== null || posts.size()<=0){
            return;//无适合文章就不缓存本周热议
        }

        //2.初始化文章的总评论量
        for (MPost post : posts) {
            String key = "day:rank:" + DateUtil.format(post.getCreated(), DatePattern.PURE_DATE_FORMAT);
            redisUtil.zSet(key, post.getId(), post.getCommentCount());

            long expireTime = getExpireTime(post.getCreated());
            redisUtil.expire(key, expireTime);

            //缓存文章的一些基本信息（id,标题，评论数量，作者）
            this.hashCachePostInfo(post, expireTime);
        }

        //3.做并集
        this.zunionstoreLast7DayForWeekRank();
    }

    /**
     * 获取本周热议数据，7天内的数据做并集
     * 本周合并每日评论数量
     */
    private void zunionstoreLast7DayForWeekRank() {
        String destKey = "week:rank";
        String currDayKey = "day:rank:" + DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT);
        List<String> otherKeys = new ArrayList<>();
        for (int i = -6; i < 0; i++) {
            String tmpKey = "day:rank:" + DateUtil.format(DateUtil.offsetDay(new Date(), i), DatePattern.PURE_DATE_FORMAT);
            otherKeys.add(tmpKey);
        }
        redisUtil.zUnionAndStore(currDayKey, otherKeys, destKey);
    }

    /**
     * 缓存文章的基本信息
     *
     * @param post
     * @param expireTime
     */
    private void hashCachePostInfo(MPost post, long expireTime) {
        String key = "post:rank:" + post.getId();
        boolean hasKey = redisUtil.hasKey(key);
        if (!hasKey) {
            redisUtil.hset(key, "post:id", post.getId(), expireTime);
            redisUtil.hset(key, "post:title", post.getTitle(), expireTime);
            redisUtil.hset(key, "post:commentCount", post.getCommentCount(), expireTime);
            redisUtil.hset(key, "post:viewCount", post.getViewCount(), expireTime);
        }
    }

    /**
     * 增加或删除评论，重新计算本周热议
     *
     * @param postId
     * @param isIncr
     */
    @Override
    public void updateCommentCountAndUnionForWeekRank(long postId, boolean isIncr) {
        String currDayKey = "day:rank:" + DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT);
        redisUtil.zIncrementScore(currDayKey, postId, isIncr ? 1 : -1);//1.更新该文章的当天评论数

        MPost post = this.getById(postId);
        this.hashCachePostInfo(post, getExpireTime(post.getCreated()));//2.缓存该文章的基本信息（有可能是第一次评论的文章）

        this.zunionstoreLast7DayForWeekRank();//3.重新计算本周热议
    }

    /**
     * 更新阅读量
     *
     * @param vo
     */
    @Override
    public void putViewCount(MPostVo vo) {
        //1.从缓存中获取阅读量viewCount,并 + 1
        String key = "post:rank:" + vo.getId();
        Integer viewCount = (Integer) redisUtil.hget(key, "post:viewCount");
        if (viewCount != null) {
            vo.setViewCount(viewCount + 1);
        } else {
            vo.setViewCount(vo.getViewCount() + 1);
        }

        boolean hasKey = redisUtil.hHasKey(key, "post:id");
        if(!hasKey){
            MPost post = this.getById(vo.getId());
            this.hashCachePostInfo(post, getExpireTime(post.getCreated()));
        }

        //2.更新数据到缓存
        redisUtil.hset(key, "post:viewCount", vo.getViewCount(),getExpireTime(vo.getCreated()));
    }

    /**
     * 获取文章过期时间
     * 7天后自动过期(15号发表，还可以存活：7 - (18(今天) - 15) =  4天)
     *
     * @param postCreated
     * @return
     */
    private long getExpireTime(Date postCreated) {
        long between = DateUtil.between(new Date(), postCreated, DateUnit.DAY);
        long expireTime = (7 - between) * 24 * 60 * 60;
        return expireTime;
    }
}
